package com.yanfan.modbus.codec;

import cn.hutool.core.util.HexUtil;
import com.alibaba.fastjson2.JSONObject;
import com.yanfan.base.session.Session;
import com.yanfan.common.annotation.SysProtocol;
import com.yanfan.common.constant.YanfanConstant;
import com.yanfan.common.core.mq.DeviceReport;
import com.yanfan.common.core.mq.MQSendMessageBo;
import com.yanfan.common.core.mq.message.DeviceData;
import com.yanfan.common.core.mq.message.DeviceDownMessage;
import com.yanfan.common.core.mq.message.FunctionCallBackBo;
import com.yanfan.common.core.mq.message.PropRead;
import com.yanfan.common.core.protocol.Message;
import com.yanfan.common.core.protocol.modbus.ModbusCode;
import com.yanfan.common.core.redis.RedisCache;
import com.yanfan.common.core.redis.RedisKeyBuilder;
import com.yanfan.common.core.thingsModel.ThingsModelSimpleItem;
import com.yanfan.common.enums.FunctionReplyStatus;
import com.yanfan.common.enums.ModbusDataType;
import com.yanfan.common.exception.ServiceException;
import com.yanfan.common.utils.CaculateUtils;
import com.yanfan.common.utils.DateUtils;
import com.yanfan.common.utils.StringUtils;
import com.yanfan.common.utils.gateway.CRC16Utils;
import com.yanfan.iot.domain.ModbusConfig;
import com.yanfan.iot.model.ThingsModels.PropertyDto;
import com.yanfan.iot.model.ThingsModels.ThingsModelValueItem;
import com.yanfan.iot.service.IThingsModelService;
import com.yanfan.modbus.model.ModbusRtu;
import com.yanfan.protocol.base.protocol.IProtocol;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.util.ReferenceCountUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

import static com.yanfan.common.core.protocol.modbus.ModbusCode.getInstance;

/**
 * @author bill
 */
@Slf4j
@Component
@SysProtocol(name = "ModbusRtu标准协议", protocolCode = YanfanConstant.PROTOCOL.ModbusRtuStandard, description = "系统内置ModbusRtu标准解析协议")
public class ModbusStandardMessageProtocol implements IProtocol {


    @Resource
    private ModbusMessageDecoder messageDecoder;
    @Resource
    private ModbusMessageEncoder messageEncoder;
    @Resource
    private RedisCache redisCache;
    @Resource
    private IThingsModelService thingsModelService;

    @Override
    public DeviceReport decode(DeviceData data, String clientId) {
        return decode(data, clientId, null);
    }

    @Override
    public DeviceReport decode(DeviceData deviceData, String clientId, Session session) {
        try {
            DeviceReport report = new DeviceReport();
            ModbusRtu message = messageDecoder.decode(deviceData);
            if (message.getmId() != 0) {
                report.setClientId(message.getMac());
                report.setMessageId(String.valueOf(message.getmId()));
                return report;
            }

            //如果返回06编码，说明是设备回复，更新对应寄存器的值，并发送通知前端
            if (message.getCode() == ModbusCode.Write06.getCode()) {
                report.setAddress(message.getAddress());
                report.setSlaveId(message.getSlaveId());
                report.setReplyMessage(message.getWriteData() + "");
                report.setClientId(deviceData.getSerialNumber());
                report.setSerialNumber(deviceData.getSerialNumber());
                report.setProductId(deviceData.getProductId());
                report.setIsReply(true);
                report.setProtocolCode(YanfanConstant.PROTOCOL.ModbusRtuStandard);
                report.setStatus(FunctionReplyStatus.SUCCESS);
                return report;
            }

            List<ThingsModelSimpleItem> values = new ArrayList<>();
            for (int i = 0; i < message.getData().length; i++) {
                ThingsModelSimpleItem things = new ThingsModelSimpleItem();
                things.setId(i + "");
                things.setSlaveId(Integer.parseInt(HexUtil.toHex(message.getSlaveId())));
                things.setTs(DateUtils.getNowDate());
                ModbusCode code = getInstance(message.getCode());
                switch (code) {
                    case Read01:
                    case Read02:
                        things.setValue(message.getBitData() + "");
                        break;
                    case Read03:
                    case Read04:
                        things.setValue(message.getData()[i] + "");
                        break;
                }
                values.add(things);
            }
            report.setThingsModelSimpleItem(values);
            report.setClientId(clientId);
            return report;
        } catch (Exception e) {
            log.error("=>解码异常,clientId:{}, hex:{}, msg: [{}]", clientId, HexUtil.encodeHexStr(deviceData.getData()), e.getMessage());
            throw new ServiceException(e.getMessage());
        }
    }

    @Override
    public FunctionCallBackBo encode(MQSendMessageBo message) {
        ModbusRtu rtu = new ModbusRtu();
        FunctionCallBackBo callBack = new FunctionCallBackBo();
        DeviceData data = message.getData();
        if (Objects.nonNull(data)) {
            Object body = data.getBody();
            if (Objects.nonNull(body) && body instanceof Message) {
                Message msg = (Message) body;
                if ((Objects.equals(msg.getMessageId(), "128") || Objects.equals(msg.getMessageId(), "129"))) {
                    callBack.setMessage(ByteBufUtil.decodeHexDump(String.valueOf(msg.getBody())));
                    callBack.setSources(String.valueOf(msg.getBody()));
                    return callBack;
                }
            }

            DeviceDownMessage downMessage = data.getDownMessage();
            if (data.getType() == 2) {
                rtu = (ModbusRtu) data.getBody();
            } else if (Objects.nonNull(data.getCode())) {
                switch (data.getCode()) {
                    case Read01:
                    case Read02:
                    case Read03:
                    case Read04:
                        PropRead prop = (PropRead) data.getBody();
                        read03_1(prop, rtu);
                        break;
                    case Write05:
                    case Write06:
                        write0506_1(downMessage, rtu);
                        break;
                }
            }
            ByteBuf out = messageEncoder.encode(rtu);
            byte[] bs = new byte[out.writerIndex()];
            out.readBytes(bs);
            ReferenceCountUtil.release(out);
            byte[] result = CRC16Utils.AddCRC(bs);
            callBack.setMessage(result);
            callBack.setSources(ByteBufUtil.hexDump(result));
        } else {
            String thingsModel = message.getThingsModel();
            ThingsModelValueItem item = JSONObject.parseObject(thingsModel, ThingsModelValueItem.class);
            ModbusConfig config = item.getConfig();
            switch (config.getModbusCode()) {
                case Read01:
                case Read02:
                case Read03:
                case Read04:
                    this.read03(config, rtu);
                    break;
                case Write05:
                    write05(config, message.getValue(), rtu);
                    break;
                case Write06:
                    write06(config, message.getValue(), rtu);
                    break;
            }
            ByteBuf out = messageEncoder.encode(rtu);
            byte[] bs = new byte[out.writerIndex()];
            out.readBytes(bs);
            ReferenceCountUtil.release(out);
            byte[] result = CRC16Utils.AddCRC(bs);
            callBack.setMessage(result);
            callBack.setSources(ByteBufUtil.hexDump(result));
        }
        return callBack;
    }

    /**
     * read03指令
     */
    private void read03_1(PropRead prop, ModbusRtu rtu) {
        rtu.setSlaveId(prop.getSlaveId() == 0 ? 1 : prop.getSlaveId());
        rtu.setCount(prop.getCount());
        rtu.setAddress(prop.getAddress());
        rtu.setCode(prop.getCode().getCode());
    }

    /**
     * writ05/06指令配置
     */
    private void write0506_1(DeviceDownMessage message, ModbusRtu rtu) {
        JSONObject values = (JSONObject) message.getBody();
        String address = values.keySet().toArray()[0].toString();
        rtu.setAddress(Integer.parseInt(address));
        String s = values.get(address).toString();
        int value;
        if (s.contains("0x")) {
            value = Integer.parseInt(s.substring(2), 16);
        } else {
            value = Integer.parseInt(s);
        }
        rtu.setWriteData(value);
        rtu.setCode(message.getCode().getCode());
        rtu.setSlaveId(message.getSlaveId() == null ? 1 : message.getSlaveId());
    }

    /**
     * 判断是否是Modbus报文，并对数据赋值.
     */
    private PropRead getCacheProp(String hex, String serialNumber) {
        //获取设备以及从机编号获取是否有缓存
        String slaveIdStr = hex.substring(0, 2);
        int slaveId = Integer.parseInt(slaveIdStr, 16);
        String cacheKey = RedisKeyBuilder.buildPropReadCacheKey(serialNumber.toUpperCase() + "_" + slaveId);
        PropRead prop = redisCache.getCacheObject(cacheKey);
        if (null != prop) {
            /*判断报文是否跟下发的指令匹配*/
            if (prop.getSlaveId() == slaveId) {
                //redisCache.deleteObject(cacheKey);
                return prop;
            }
        }
        return new PropRead();
    }

    private String parseValue(PropertyDto dto, ModbusRtu message) {
        String value = "";
        String parseType = dto.getParseType();
        String hexValue = CaculateUtils.subHexValue(message.getHex());
        Long val = Long.parseLong(hexValue, 16);
        byte[] bytes = ByteBufUtil.decodeHexDump(hexValue);
        if (StringUtils.isNotEmpty(parseType)) {
            ModbusDataType type = ModbusDataType.convert(parseType);
            switch (type) {
                case U_SHORT:
                    value = CaculateUtils.toUnSign16(val);
                    break;
                case SHORT:
                case LONG_ABCD:
                    value = val + "";
                    break;
                case LONG_CDAB:
                    value = CaculateUtils.toSign32_CDAB(val);
                    break;
                case U_LONG_ABCD:
                    value = CaculateUtils.toUnSign32_ABCD(val);
                    break;
                case U_LONG_CDAB:
                    value = CaculateUtils.toUnSign32_CDAB(val);
                    break;
                case FLOAT_ABCD:
                    value = CaculateUtils.toFloat32_ABCD(bytes) + "";
                    break;
                case FLOAT_CDAB:
                    value = CaculateUtils.toFloat32_CDAB(bytes) + "";
                    break;
            }
        }
        return value;
    }

    /**
     * read03指令
     */
    private void read03(ModbusConfig modbusConfig, ModbusRtu rtu) {
        rtu.setSlaveId(modbusConfig.getSlave());
        rtu.setCount(modbusConfig.getQuantity());
        rtu.setAddress(modbusConfig.getAddress());
        rtu.setCode(modbusConfig.getModbusCode().getCode());
    }

    /**
     * writ05/06指令配置
     */
    private void write05(ModbusConfig modbusConfig, String value, ModbusRtu rtu) {
        int data;
        if (value.contains("0x")) {
            data = Integer.parseInt(value.substring(2), 16);
        } else {
            data = Integer.parseInt(value);
        }
        rtu.setWriteData(data == 1 ? 0xFF00 : 0x0000);
        rtu.setAddress(modbusConfig.getAddress());
        rtu.setCode(modbusConfig.getModbusCode().getCode());
        rtu.setSlaveId(modbusConfig.getSlave());
    }

    /**
     * writ05/06指令配置
     */
    private void write06(ModbusConfig modbusConfig, String value, ModbusRtu rtu) {
        rtu.setWriteData(Integer.parseInt(value));
        rtu.setAddress(modbusConfig.getAddress());
        rtu.setCode(modbusConfig.getModbusCode().getCode());
        rtu.setSlaveId(modbusConfig.getSlave());
    }


}
